package recipese.service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringEscapeUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.safety.Whitelist;
import org.jsoup.select.Elements;
import org.springframework.util.StringUtils;

import recipese.bean.Ingredient;
import recipese.bean.Recipe;


public class RemoteRecipeService {

	private final List<String> nameRules = new ArrayList<String>();
	private final List<String> ingredientsRules = new ArrayList<String>();
	private final List<String> instructionsRules = new ArrayList<String>();

	public RemoteRecipeService() {
		nameRules.add(".fn");
		nameRules.add(".pageHead [itemprop=name]");
		nameRules.add("[itemtype=http://data-vocabulary.org/Recipe] [itemprop=name]");
		nameRules.add("#recipe-callout h2");

		ingredientsRules.add("[itemprop=ingredient]");
		ingredientsRules.add("div.ingredient p|split-br");
		ingredientsRules.add(".ingredient");
		ingredientsRules.add("[itemtype=http://data-vocabulary.org/Recipe] li");

		instructionsRules.add("[itemprop=instructions] li");
		instructionsRules.add(".instructions li");
		instructionsRules.add(".instructions");
		instructionsRules.add(".directions li");
		instructionsRules.add("[itemtype=http://data-vocabulary.org/Recipe] [itemprop=instructions] p");
		instructionsRules.add("[itemtype=http://data-vocabulary.org/Recipe] [itemprop=instructions]");
		instructionsRules.add("#recipe-method p");
	}

	public Recipe parse(String url) {
		final Recipe recipe = new Recipe();

		try {
			Document doc = Jsoup.connect(url).userAgent("Mozilla/5.0 (X11; U; Linux amd64; en-US; rv:5.0) Gecko/20110619 Firefox/5.0").get();

			recipe.setName(StringEscapeUtils.unescapeHtml4(getName(doc, nameRules)));

			recipe.getIngredients().addAll(getIngredients(doc, ingredientsRules));

			recipe.setInstructions(StringEscapeUtils.unescapeHtml4(getInstructions(doc, instructionsRules)));
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return recipe;
	}

	public List<Ingredient> getIngredients(Document doc, List<String> rules) {
		final Map<String, Ingredient> ingredients = new LinkedHashMap<String, Ingredient>();
		
		for (String rule : rules) {
			final String[] parts = rule.split("\\|");
			final String selector = parts[0];
			
			String options = null; 
			if (parts.length > 1) {
				options = parts[1]; 
			}
			
			final Elements elements = doc.select(selector);
			if (elements != null) {
				for(Element element : elements) {
					
					// handle advanced rules
					if ("split-br".equals(options)) {

						final Whitelist whitelist = Whitelist.none();
						whitelist.addTags("br");
						
						final String ingredientsText = Jsoup.clean(element.html(), whitelist);
						final String[] ingredientsTextTokens = ingredientsText.split("<br />");
						
						for (final String text : ingredientsTextTokens) {
							if (StringUtils.hasText(text)) {
								final Ingredient ingredient = new Ingredient();
								ingredient.setText(StringEscapeUtils.unescapeHtml4(text));
								ingredients.put(text, ingredient);
							}
						}
						
					} else {
						final String text = element.text();
						
						if (StringUtils.hasText(text)) {
							final Ingredient ingredient = new Ingredient();
							ingredient.setText(StringEscapeUtils.unescapeHtml4(text));
							ingredients.put(text, ingredient);
						}
					}
				}
				
				if (ingredients.size() > 0) {
					return new ArrayList<Ingredient>(ingredients.values());
				}
			}
		}
		
		return new ArrayList<Ingredient>();
	}
	
	public String getName(Document doc, List<String> rules) {
		
		for (String rule : rules) {
			Elements elements = doc.select(rule);
			if (elements != null) {
				if (StringUtils.hasText(elements.text())) {
					return elements.text();
				}
			}
		}
		
		return null;
	}
	
	public String getInstructions(Document doc, List<String> rules) {
		
		for (String rule : rules) {
			Elements elements = doc.select(rule);
			if (elements != null) {
				if (elements.size() > 1) {
					final StringBuilder builder = new StringBuilder();
					for(Element element : elements) {
						builder.append(element.text()).append(" \r\n\r\n");
					}
					
					final String results = builder.toString();
					if (StringUtils.hasText(results)) {
						return results;
					}
				} else {
					if (StringUtils.hasText(elements.text())) {
						return elements.text();
					}
				}
			}
		}
		
		return null;
	}
	
}
